feat: add developer-gated Prompt Shortcuts [risk:high] - #575
Conversation
Add scoped private/shared shortcut catalogs, lazy LoroDoc bodies, durable local publication recovery, settings authoring and Storybook prototypes. Slash invocation and composer send integration remain follow-up work. Model: gpt-6-astra
…sk:high] Settings now reads as the same surface as Agent Roles and MCP: one dialog header/scroll/footer editor built from the shared `form-primitives` grammar, Radix selects for the three scope axes, a Switch for sharing with its visibility warning, and the shared catalog row. Adds scope pills, per-row publication state, a read-only view for another member's shared Shortcut, and disabled mention kinds that name the scope axes they still need. Also carries this branch's local-first storage work: a separate local ledger, write intents and immutable publication jobs, so a pending publication stays durable without ever blocking Save or Delete. Slash invocation, the variable argument tray and composer send integration remain follow-up work. Model: claude-opus-5
Scope moves out of its own section and sits inline above the prompt input, each axis naming itself in its trigger, with the explanation folded into the prompt hint. The description field says what it is and where it shows, a variable's default sits beside its name instead of under it, and the prompt box starts shorter. Model: claude-opus-5
A default value is usually a few words, so the field starts at a single row and tracks its content instead of reserving two. `AutoGrowTextarea` joins the shared settings primitives, and re-measures on width changes because wrapping is what decides the row count. Model: claude-opus-5
Add independent Slash discovery, cancellable snapshot selection, inline invocation parameters, isolated draft recovery, and ordered prompt compilation with expand-and-edit support. Model: gpt-6
Reuse the shared composer mention portal within the nearest modal and constrain desktop and mobile positioning to its bounds. Cover real Shortcut creation dialogs in Storybook and modal selection in regression tests. Model: gpt-6
…sk:medium]
The tray now reads as the settings variable editor it mirrors: the `!{name}`
token beside its value, growing from one row, a missing value tinting its own
token instead of adding an asterisk. Desktop keeps a section header with the
dismiss action; mobile keeps the sheet with a full-width Done.
"Expand and edit" is removed. It existed to change a template's body, which
Settings already does — durably, for every invocation — and it paid for that
with a whole draft vocabulary of its own: generated unresolved markers, literal
ranges that stop substituted text being rescanned, their persistence and undo
rules, and an extra send-time check for leftover markers. A chip is atomic
again, so all of that goes with it.
Model: claude-opus-5
The catalog row was four left-aligned lines against an empty right half. It is now two: identity reads down the left, and what the author set — scope, variable count — plus what is happening to it sit against the right edge. Both halves wrap rather than switching on a viewport breakpoint, because settings render in a panel far narrower than the window. Status copy shortens to phrases that fit that column. The tray drops the "fill these variables" line between the prompt and the fields and sits against the invocation instead. The chip's own badge and the tinted token already name what is missing; a third sentence only pushed the fields away from the thing they belong to. Model: claude-opus-5
A Shortcut carries an optional `emoji`, normalized on the way in and projected into the index so the list can show it without loading a body. It replaces the `/` glyph in the catalog tile; unset renders the default rather than an empty slot. The emoji control itself moves out of the Agent Role editor into `settings/emoji-field.tsx`, so the two editors cannot grow two of them. Also drops the `!N` badge painted over an invocation chip — the chip already turns red and the tray names the values — and with it the mention primitive's `badge` slot, which had no other caller. While a parameter tray is open the composer stops reserving its blank writing rows: those rows are room for writing, and reserving them above the tray left a band of empty box between the `/command` and the fields it belongs to. Model: claude-opus-5
…k:medium] Four things the composer and the catalog were getting wrong: Clearing the prompt to empty dropped focus. The reset remounts the mention tree to re-arm the hydrators, which replaces the textarea node; restoring focus was conditional on the caller having passed a ref, and the settings template editor passes none. The component keeps its own ref now and hands the node to the forwarded one as well. The catalog reported background publication. A local save is already durable and the runtime retries on its own, so neither state is something a reader can act on from a row. The only status left is the one its author must repair: a reference that no longer fits the saved scope. The `/` menu shows each Shortcut's own emoji, the way an Agent Role's row does. A chip now carries the values that were filled into it (`shortcutChipText`, applied on tray close), capped by code points so a truncation cannot split a surrogate pair. A draft of three identical `/review` chips was not something anyone could check before sending. Both forms of the text stay valid, so a draft stored before its values still restores. Model: claude-opus-5
A Shortcut is a Prompt and a scope now, nothing else. `!{name}` is ordinary
text: stored, expanded and sent exactly as written.
Gone from the model: the `variables` field, its schema and byte limits, the
index's `variableCount`, placeholder parsing and escaping, value injection, the
`missing_variables` error code, and `updateShortcutInvocation` (which existed
only to migrate values across a reload). `ShortcutInvocation` is `{ id,
snapshot }`, so `expandShortcut` is a straight lowering of the snapshot's text
and mentions.
Gone from the UI: the Settings variables section, the composer's parameter tray
and bottom sheet, their send gate, the chip's missing-count colouring, and the
value label a chip carried. What remains of the composer bridge is
`shortcut-invocation-status.tsx` — why an inserted chip cannot be sent, which is
about live context rather than anything the caller types.
The invocation schema is deliberately not strict: a draft stored while values
existed still restores, minus the ignored key.
Model: claude-opus-5
Default the opt-in off and share the developer-mode gate across settings navigation, direct panel access, mention selection and the workspace runtime. Preserve saved data and cover toggle transitions and late selection results. Model: gpt-6
Clear the Shortcut checkpoint before successful landing navigation, fenced by composer mount and draft identity so late session acceptance cannot delete or navigate a replacement draft. Add deterministic recovery regressions and satisfy the branch formatting gate. Model: gpt-6
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
Preserve composer submission ownership and Shortcut preparation while integrating main. Clear retired accepted Shortcut checkpoints only when their submitted version remains current. Model: gpt-6
Model: gpt-6
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 362a08f32b
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| await this.put({ | ||
| entry, | ||
| published: current?.operation ? current.published : entry, | ||
| operation: 'delete', | ||
| deleted: true, |
There was a problem hiding this comment.
Revoke the published body on delete
When an author deletes a successfully published Shortcut from the authoring device, entry is the local working projection, while preparePublication() uploaded a fresh body and stored that distinct projection in current.published. Persisting the caller's working entry here therefore makes publishRecord() call revokeShortcut with a bodyDocId that was never activated; a host enforcing the request's body/visibility fence cannot revoke it, so a shared Shortcut remains accessible and every retry repeats the wrong request. Enqueue the published projection for remote deletion while retaining the local deletion state.
AGENTS.md reference: packages/shared/src/prompt-shortcuts/AGENTS.md:L22-L25
Useful? React with 👍 / 👎.
| await this.recover(); | ||
| const existing = this.publication(id); | ||
| const current = this.get(id); | ||
| if (existing && !current?.deleted) return existing; |
There was a problem hiding this comment.
Preserve an existing publication job through deletion
If Delete is selected while a save/activation job is pending or its reply was lost, current.deleted makes this condition skip the durable existing job; the method then overwrites the publication row with the delete record below. Consequently PromptShortcutRuntime.flush() never sees the obsolete save job and never calls settle(), losing the only identity capable of fencing a staged or already-active body and potentially leaving the supposedly deleted publication or its quota reservation behind. Preserve and settle the existing job before replacing it with deletion.
AGENTS.md reference: packages/shared/src/prompt-shortcuts/AGENTS.md:L36-L37
Useful? React with 👍 / 👎.
Remove the superseded design prototype, unused editable annotations and replacement API, test-only composer compiler and publication helper. Keep real component stories and move stale/duplicate invocation assertions onto the actual send path. Consolidate repeated documentation and preserve locale ordering. Ablations retain semantic Undo and stale-preparation fences: negative controls fail when either protection is removed. Full workspace typecheck and test:ci pass; installed-workspace lint, formatting, docs, i18n and boundary checks pass. Model: gpt-6
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a4e4e11477
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| <StuckConnectionBannerContainer /> | ||
| {workspaceReady ? <DesktopSettingsModal /> : null} | ||
| </WorkspaceRuntimeShell> | ||
| <PromptShortcutProvider enabled={workspaceReady}> |
There was a problem hiding this comment.
Gate the Shortcut runtime on the workspace window owner
When Prompt Shortcuts are enabled in Electron, every primary or auxiliary window mounts this provider even though owner already identifies the single workspace window permitted to run singleton background work. Opening a second window for the same workspace therefore creates another PromptShortcutRuntime over the same account/workspace IndexedDB and lets both instances drain the same publication outbox, causing competing ledger writes and duplicate or conflicting cloud publication attempts. Mount the runtime only for the workspace owner or provide a genuinely shared single owner.
AGENTS.md reference: packages/components/src/providers/AGENTS.md:L41-L47
Useful? React with 👍 / 👎.
| getCandidates: (term, limit) => | ||
| selectPromptShortcutCandidates({ entries, context, loading }, term, t, limit).map( | ||
| (candidate) => { |
There was a problem hiding this comment.
Wire live dependency resolution into Shortcut discovery
For every Shortcut containing a file, Skill, Role, issue, or PR, this production call omits resolveDependency, so selectPromptShortcutCandidates uses unverifiedShortcutDependency and always marks the entry unknown; non-exact matches disappear and exact matches are disabled. The selection, status, and compilation paths likewise never provide a resolver, so users can author reference-bearing Shortcuts but can never insert or send them even when all dependencies are available. Supply the live dependency resolver throughout this composer path.
AGENTS.md reference: packages/components/src/components/mentions/AGENTS.md:L114-L116
Useful? React with 👍 / 👎.
…edium] Remove dedicated Shortcut suites, stories and feature-only additions to shared fixtures. Retain existing general composer and mention tests, and document the temporary coverage scope without changing production behavior. Model: gpt-6
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3aca38b3f9
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| if (trigger === '/') { | ||
| const directCategories = categories.filter((category) => category.directTrigger === '/'); |
There was a problem hiding this comment.
Hide ACP commands from inline slash menus
When Prompt Shortcuts are enabled and the composer already contains ordinary text, / remains active for inline Shortcut insertion, but this branch also includes every ACP command category. Selecting one then replaces only the slash-to-caret query, producing text such as explain this /review; the agent receives an ordinary message instead of the advertised whole-prompt command. Filter ACP commands out whenever the slash trigger is not the entire prompt.
AGENTS.md reference: packages/components/src/components/mentions/AGENTS.md:L117-L118
Useful? React with 👍 / 👎.
| export function promptShortcutDatabaseName(workspaceId: string, userId: string): string { | ||
| return `${PROMPT_SHORTCUT_DATA_PREFIX}${encodeURIComponent(workspaceId)}:${encodeURIComponent(userId)}`; |
There was a problem hiding this comment.
Register Shortcut databases for hard-reset fallback
On engines where indexedDB.databases() is unavailable or throws, a crash-screen hard reset cannot discover this new account/workspace-specific database name. clearAllLodyLocalData() only falls back to static known names, and the later RuntimeProvider extras contain only the workspace repo and cursor databases, so saved Prompt bodies and outbox data survive the promised full wipe/sign-out. Record these dynamic database names for the hard-reset fallback or pass them explicitly before identity storage is cleared.
AGENTS.md reference: packages/components/src/lib/AGENTS.md:L34-L37
Useful? React with 👍 / 👎.
Model: gpt-6
Preserve Agent Role schema reconciliation inside the Shortcut provider. Model: gpt-6
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9cc11cc4ff
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| useEffect(() => { | ||
| if (!runtime) return undefined; | ||
| const retry = () => { | ||
| void runtime.flush(); | ||
| if (directory) void runtime.setDirectory(directory); | ||
| }; | ||
| window.addEventListener('online', retry); | ||
| return () => window.removeEventListener('online', retry); |
There was a problem hiding this comment.
Schedule retries after failed publication
When a stage, activation, or index-sync request fails while the browser remains online (for example, a transient 5xx), PromptShortcutRuntime.flush() records the error and stops after that one attempt, while this provider retries only on a future online event. Since the settings surface no longer exposes the runtime retry callback, the locally successful save can remain unpublished indefinitely until the user reloads, toggles connectivity, or performs another save. Add a bounded/backoff retry path for failed outbox records rather than relying exclusively on offline-to-online transitions.
Useful? React with 👍 / 👎.
| const siblings = this.list().filter( | ||
| (row) => | ||
| !row.deleted && row.entry.id !== value.id && row.entry.visibility === value.visibility |
There was a problem hiding this comment.
Enforce catalog limits across visibility domains
When an author already has a Shortcut under the other visibility, this predicate excludes it from both slug uniqueness and item/byte quota checks. The local save therefore succeeds for duplicate slugs or a catalog exceeding the cloud publication contract, closes the editor, and only the later background activation rejects it; with publication errors hidden, the entry appears normal locally but never becomes available to its intended audience. Count all non-deleted authored Shortcuts here, regardless of whether their index is private or workspace-visible.
Useful? React with 👍 / 👎.
Remove invocation chips, dedicated draft checkpoints and send compilation. Preserve cancellable selection and ordinary mention ranges. Model: gpt-6
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 95dfd48568
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| }; | ||
|
|
||
| export type CloudApi = { | ||
| promptShortcuts: { |
There was a problem hiding this comment.
Add the required Spec and Agent Note
This introduces a public cloud protocol and new durable persistence, publication, access, and deletion guarantees, but the changed-file list contains neither an owning draft Spec nor an Agent Note. Without those records, the intended cross-client and cloud contracts are not reviewable or traceable; add both in this change as required by the repository’s governance rules.
AGENTS.md reference: AGENTS.md:L15-L21
Useful? React with 👍 / 👎.
Risk: 🔴 high | Confidence: medium — private/shared persistence remains; real gateway validation is incomplete.
Prompt Shortcuts save scoped private or workspace-shared Prompts, default off behind Developer mode and the Beta switch. Selecting one from
/loads and rechecks its body, then replaces the query with ordinary editable text and ordinary mention ranges. Selection cancellation preserves newer input.There is no Shortcut chip, invocation snapshot, dedicated composer draft store, custom Undo history or Shortcut send compiler. Ordinary composer persistence and send expansion own the inserted content. Settings reuse composer mentions and exclude recursive Shortcuts, nested ACP commands and session references.
All PR-added tests, Storybook and repository documentation remain removed. Validation used temporary tests (not committed): plain insertion and offsets, cancellation, version changes and React integration, plus 68 existing mention/submission tests. Full workspace typecheck, targeted lint (no errors), formatting and i18n passed.
Limits: actual dependency resolution, full sync conflict repair, cache eviction and real gateway E2E remain incomplete. The outer check:affected wrapper still fails on the existing indexed OSS revision mismatch. Old dedicated invocation checkpoints are no longer restored; this change does not delete their local database. No parent gitlink change is committed.